"""
增强服务层测试基类
提供更丰富的服务层测试功能和便捷方法
"""

from typing import Any, Dict, List, Optional
from unittest.mock import Mock, patch, MagicMock
from sqlalchemy.orm import Session

from tests.services.base_service_test import BaseServiceTest
from tests.factories.test_data_factory import (
    create_user_create_data,
    create_role_create_data,
    create_permission_create_data
)
from tests.utils import (
    create_mock_user,
    create_mock_role,
    create_mock_permission
)


class EnhancedServiceTest(BaseServiceTest):
    """增强服务层测试基类"""
    
    def setup_method(self):
        """每个测试方法前的设置"""
        super().setup_method()
        
        # 初始化测试数据工厂
        self.test_data_factory = None  # 在需要时初始化
    
    # 模拟数据库操作增强方法
    
    def setup_mock_query_with_pagination(self, model_class, items: List[Any], total: int):
        """设置模拟分页查询"""
        mock_query = Mock()
        
        # 分页方法
        mock_query.offset = Mock(return_value=mock_query)
        mock_query.limit = Mock(return_value=mock_query)
        mock_query.order_by = Mock(return_value=mock_query)
        
        # 结果方法
        mock_query.all = Mock(return_value=items)
        mock_query.first = Mock(return_value=items[0] if items else None)
        mock_query.one_or_none = Mock(return_value=items[0] if items else None)
        mock_query.count = Mock(return_value=total)
        
        # 过滤方法
        mock_query.filter = Mock(return_value=mock_query)
        mock_query.filter_by = Mock(return_value=mock_query)
        mock_query.where = Mock(return_value=mock_query)
        
        self.mock_db.query = Mock(return_value=mock_query)
        return mock_query
    
    def setup_mock_query_with_search(self, model_class, items: List[Any]):
        """设置模拟搜索查询"""
        mock_query = Mock()
        
        # 搜索相关方法
        mock_query.filter = Mock(return_value=mock_query)
        mock_query.filter_by = Mock(return_value=mock_query)
        mock_query.where = Mock(return_value=mock_query)
        mock_query.contains = Mock(return_value=mock_query)
        mock_query.ilike = Mock(return_value=mock_query)
        
        # 结果方法
        mock_query.all = Mock(return_value=items)
        mock_query.first = Mock(return_value=items[0] if items else None)
        
        self.mock_db.query = Mock(return_value=mock_query)
        return mock_query
    
    def setup_mock_transaction(self):
        """设置模拟事务操作"""
        self.mock_db.commit = Mock()
        self.mock_db.rollback = Mock()
        self.mock_db.flush = Mock()
    
    def setup_mock_bulk_operations(self, model_class, items: List[Any]):
        """设置模拟批量操作"""
        self.mock_db.bulk_insert_mappings = Mock()
        self.mock_db.bulk_update_mappings = Mock()
        self.mock_db.bulk_save_objects = Mock(return_value=items)
    
    # 服务层测试通用方法
    
    def test_service_method_success(self, service_method, *args, **kwargs):
        """测试服务方法成功执行"""
        result = service_method(*args, **kwargs)
        assert result is not None
        return result
    
    def test_service_method_with_exception(self, service_method, expected_exception,
                                         *args, **kwargs):
        """测试服务方法抛出异常"""
        with patch.object(service_method.__self__, service_method.__name__,
                         side_effect=expected_exception):
            try:
                service_method(*args, **kwargs)
                assert False, "Expected exception was not raised"
            except type(expected_exception):
                pass  # 异常按预期抛出
    
    def test_service_method_with_mock_db_error(self, service_method,
                                             error_type: type = Exception,
                                             *args, **kwargs):
        """测试服务方法处理数据库错误"""
        # 模拟数据库错误
        self.mock_db.commit.side_effect = error_type("Database error")
        
        try:
            service_method(*args, **kwargs)
            assert False, "Expected database error was not raised"
        except error_type:
            pass  # 数据库错误按预期抛出
        except Exception as e:
            # 如果抛出了其他异常，验证是否包含数据库错误
            assert "database" in str(e).lower() or "db" in str(e).lower()
    
    # 断言方法增强
    
    def assert_service_result(self, result: Any, expected_type: type,
                            expected_attributes: Optional[Dict[str, Any]] = None):
        """断言服务结果"""
        assert isinstance(result, expected_type), f"Expected {expected_type}, got {type(result)}"
        
        if expected_attributes:
            for attr, expected_value in expected_attributes.items():
                assert hasattr(result, attr), f"Result missing attribute: {attr}"
                actual_value = getattr(result, attr)
                assert actual_value == expected_value, \
                    f"Attribute {attr}: expected {expected_value}, got {actual_value}"
    
    def assert_service_list_result(self, result: List[Any], expected_count: Optional[int] = None,
                                 expected_item_type: Optional[type] = None):
        """断言服务列表结果"""
        assert isinstance(result, list), f"Expected list, got {type(result)}"
        
        if expected_count is not None:
            assert len(result) == expected_count, \
                f"Expected {expected_count} items, got {len(result)}"
        
        if expected_item_type is not None:
            for item in result:
                assert isinstance(item, expected_item_type), \
                    f"Expected {expected_item_type}, got {type(item)}"
    
    def assert_mock_db_called_with_filters(self, mock_query, expected_filters: List[str]):
        """断言模拟数据库查询使用了预期的过滤条件"""
        call_args_list = mock_query.filter.call_args_list
        assert len(call_args_list) >= len(expected_filters), \
            f"Expected at least {len(expected_filters)} filter calls, got {len(call_args_list)}"
    
    def assert_mock_db_called_with_pagination(self, mock_query, expected_offset: int,
                                            expected_limit: int):
        """断言模拟数据库查询使用了预期的分页参数"""
        # 检查 offset 调用
        offset_calls = [call for call in mock_query.offset.call_args_list 
                       if call[0][0] == expected_offset]
        assert len(offset_calls) > 0, \
            f"Expected offset({expected_offset}) call not found"
        
        # 检查 limit 调用
        limit_calls = [call for call in mock_query.limit.call_args_list 
                      if call[0][0] == expected_limit]
        assert len(limit_calls) > 0, \
            f"Expected limit({expected_limit}) call not found"
    
    def assert_mock_db_transaction_called(self):
        """断言模拟数据库事务方法被调用"""
        self.mock_db.commit.assert_called()
        # 注意：在某些情况下可能不会调用 flush，所以不强制检查
    
    def assert_mock_db_rollback_called(self):
        """断言模拟数据库回滚被调用"""
        self.mock_db.rollback.assert_called()
    
    # 性能测试方法
    
    def measure_service_method_performance(self, service_method, *args,
                                         iterations: int = 100, **kwargs):
        """测量服务方法性能"""
        import time
        
        start_time = time.time()
        for _ in range(iterations):
            service_method(*args, **kwargs)
        end_time = time.time()
        
        execution_time = end_time - start_time
        avg_time = execution_time / iterations
        
        return {
            "total_time": execution_time,
            "average_time": avg_time,
            "iterations": iterations
        }
    
    # 数据一致性测试方法
    
    def test_data_consistency_after_operation(self, operation_method,
                                            pre_check_method, post_check_method,
                                            *args, **kwargs):
        """测试操作前后的数据一致性"""
        # 操作前检查
        pre_state = pre_check_method()
        
        # 执行操作
        result = operation_method(*args, **kwargs)
        
        # 操作后检查
        post_state = post_check_method()
        
        # 验证一致性（具体验证逻辑应在具体测试中实现）
        return {
            "pre_state": pre_state,
            "post_state": post_state,
            "result": result
        }
    
    # 并发测试方法
    
    def test_concurrent_service_operations(self, service_method, *args,
                                        concurrent_count: int = 5, **kwargs):
        """测试并发服务操作"""
        import threading
        import concurrent.futures
        
        results = []
        exceptions = []
        
        def worker():
            try:
                result = service_method(*args, **kwargs)
                results.append(result)
                return result
            except Exception as e:
                exceptions.append(e)
                raise
        
        # 使用线程池执行并发操作
        with concurrent.futures.ThreadPoolExecutor(max_workers=concurrent_count) as executor:
            futures = [executor.submit(worker) for _ in range(concurrent_count)]
            concurrent.futures.wait(futures)
        
        return {
            "results": results,
            "exceptions": exceptions,
            "success_count": len(results),
            "failure_count": len(exceptions)
        }
    
    # 依赖注入测试方法
    
    def test_service_with_dependency_mock(self, service_class, dependency_mocks: Dict[str, Mock],
                                        test_method_name: str, *args, **kwargs):
        """测试带有依赖模拟的服务"""
        # 创建服务实例
        service_instance = service_class(self.mock_db)
        
        # 注入依赖模拟
        for attr_name, mock_obj in dependency_mocks.items():
            setattr(service_instance, attr_name, mock_obj)
        
        # 调用测试方法
        test_method = getattr(service_instance, test_method_name)
        result = test_method(*args, **kwargs)
        
        return result
    
    # 业务逻辑测试方法
    
    def test_business_rule_validation(self, service_method, validation_rules: List[Dict[str, Any]],
                                    *args, **kwargs):
        """测试业务规则验证"""
        results = []
        
        for rule in validation_rules:
            try:
                # 应用规则特定的参数
                rule_args = list(args)
                rule_kwargs = kwargs.copy()
                rule_kwargs.update(rule.get("params", {}))
                
                # 执行服务方法
                result = service_method(*rule_args, **rule_kwargs)
                results.append({
                    "rule": rule,
                    "result": result,
                    "exception": None
                })
            except Exception as e:
                results.append({
                    "rule": rule,
                    "result": None,
                    "exception": e
                })
        
        return results